<script lang='ts' setup>
import * as handPoseDetection from '@tensorflow-models/hand-pose-detection';
import * as tf from '@tensorflow/tfjs';
import { find, findIndex, forEach, head, includes, isEmpty, isEqual, last, map } from 'lodash';
import { Random } from 'mockjs';
import { onMounted, ref } from 'vue';
import { link_map_finger } from './finger';
import Three from './three.vue';

// 容器大小
const width = ref(document.body.clientWidth);
const height = ref(document.body.clientHeight);

const videoDom = ref();
const canvasDom = ref();

const handsData = ref();

const model = handPoseDetection.SupportedModels.MediaPipeHands;
const detectorConfig = {
  runtime: 'tfjs',
  modelType: 'full'
} as any;

onMounted(async () => {
  tf.getBackend();
  const detector = await handPoseDetection.createDetector(model, detectorConfig);
  // 获取视频标签
  const video = videoDom.value;
  // 获取画布标签
  const canvas = canvasDom.value;

  // 设置视频源为摄像头
  navigator.mediaDevices.getUserMedia({ video: true }).then((stream) => {
    video.srcObject = stream;
  });

  video.addEventListener('play', () => {
    const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;

    const animate = async () => {
      // 画笔颜色
      const color = 'blue' || Random.color();
      const hands: handPoseDetection.Hand[] = await detector.estimateHands(video);

      handsData.value = hands;
      requestAnimationFrame(animate);
      ctx.clearRect(0, 0, video.width, video.height);
      forEach(hands, hand_data => {
        forEach(map(hand_data?.keypoints), (keypoint) => {
          // 绘制节点
          ctx.beginPath();
          ctx.fillStyle = color;
          ctx.arc(keypoint.x, keypoint.y, 10, 0, 2 * Math.PI);
          ctx.fill();
          // 绘制连线
          const node_y = findIndex(link_map_finger, i => includes(i, keypoint.name));
          const node_x = findIndex(link_map_finger[node_y], i => isEqual(i, keypoint.name));
          const node = keypoint?.name === head(head(link_map_finger)) ?
            find(hand_data?.keypoints, i => isEqual(i.name, last(head(link_map_finger)))) :
            find(hand_data?.keypoints, i => isEqual(i.name, link_map_finger[node_y][node_x - 1]));

          if (!isEmpty(node)) {
            ctx.strokeStyle = color;
            ctx.lineWidth = 4;
            ctx.moveTo(node.x, node.y);
            ctx.lineTo(keypoint.x, keypoint.y);
            ctx.stroke();
          }
        });
      });
    }

    animate();
  })
});

</script>

<template>
  <video class='video' ref='videoDom' :width='width' :height='height' autoplay></video>
  <canvas class='canvas' ref='canvasDom' :width='width' :height='height'></canvas>
  <Three :handsData='handsData'  />
</template>

<style lang='less' src='./style.less' scoped />
